/*!
 * \brief Class event defines an object that has a signaling mechanism
 *
 * Class event defines objects that has a signaling mechanism that when raises
 * notifies all the stored delegates, it doesn't allow multiple instances of the
 * same delegate, and the delegates may have different priorities
 */
template< RETURN_TYPE_TEMPLATE COMMA_SEPARATOR ARGUMENTS_TYPES_TEMPLATE >
class Event ARGUMENTS_SPECIALIZATION
{
private:
	//============================== TYPE DEFINITIONS =========================
	typedef Delegate< RETURN_TYPE COMMA_SEPARATOR ARGUMENTS_TYPES > DelegateType;

	struct DelegatePriority
	{
		DelegateType	m_dlgt;
		u32_t			m_u_priority;

		DelegatePriority( ) :
			 m_dlgt( )
			,m_u_priority( std::numeric_limits< u32_t >::max( ) )
		{
		}

		DelegatePriority( const DelegatePriority& ar_dp ) :
			 m_dlgt( ar_dp.m_dlgt )
			,m_u_priority( ar_dp.m_u_priority )
		{
		}

		DelegatePriority& operator= ( const DelegatePriority& ar_dp )
		{
			new( this ) DelegatePriority( ar_dp );
			return *this;
		}

		bool_t operator < ( const DelegatePriority& ar_other_dp ) const
		{
			return this->m_u_priority < ar_other_dp.m_u_priority;
		}
	};

	//================================ DATA MEMBERS ===========================

	//! Container which responsibility is to ensure all the delegates are unique in the event
	CSet< DelegateType >				m_s_unique_dlgt;

	//! Container which responsibility is to keep the delegates ordered by priority
	CMultiSet< DelegatePriority >		m_ms_priority_dlgt;

public:
	//================================== METHODS ==============================
	//! Default constructor
	Event( )
	{
	}

	//! Destructor
	~Event( )
	{
	}

	//! Copy constructor
	Event( const Event& ar_event ) :
		 m_s_unique_dlgt( ar_event.m_s_unique_dlgt )
		,m_ms_priority_dlgt( ar_event.m_ms_priority_dlgt )
	{
	}

	//! Assignment Operator
	Event& operator = ( const Event& ar_event )
	{
		new( this ) Event( ar_event );
		return *this;
	}

	//! Tells if the event has any delegate registered or not
	bool_t IsEmpty( void ) const
	{
		return m_s_unique_dlgt.empty( );
	}

	//! Removes all the delegates from the event
	void Clear( void )
	{
		m_ms_priority_dlgt.clear( );
		m_s_unique_dlgt.clear( );
	}

	//! Returns the number of delegates registered in the event
	u32_t Size( void ) const
	{
		return m_s_unique_dlgt.size( );
	}

	//! Register a free function as delegate in the event
	bool_t Register( RETURN_TYPE (*ap_func) ( ARGUMENTS_TYPES ), 
					 u32_t au_priority = std::numeric_limits< u32_t >::max )
	{
		DelegateType dlgt;
		dlgt.Set( ap_func );

		std::pair< CSet< DelegateType >::iterator, bool_t > inserted;
		inserted = m_s_unique_dlgt.insert( dlgt );
		if( !inserted.second )
			return false;

		DelegatePriority dp;
		dp.m_dlgt = dlgt;
		dp.m_u_priority = au_priority;
		m_ms_priority_dlgt.insert( dp );

		return true;
	}

	//! Register a method as delegate in the event
	template< typename ObjType, typename ClassType >
	bool_t Register( ObjType& ap_obj, RETURN_TYPE ( ClassType::*ap_mem_fun )( ARGUMENTS_TYPES ), 
					 u32_t au_priority = std::numeric_limits< u32_t >::max )
	{
		DelegateType dlgt;
		dlgt.Set( ap_obj, ap_mem_fun );

		std::pair< CSet< DelegateType >::iterator, bool_t > inserted;
		inserted = m_s_unique_dlgt.insert( dlgt );
		if( !inserted.second )
			return false;

		DelegatePriority dp;
		dp.m_dlgt = dlgt;
		dp.m_u_priority = au_priority;
		m_ms_priority_dlgt.insert( dp );

		return true;
	}

	//! Register a const method as delegate in the event
	template< typename ObjType, typename ClassType >
	bool_t Register( ObjType& ap_obj, RETURN_TYPE ( ClassType::*ap_mem_fun )( ARGUMENTS_TYPES ) const, 
					 u32_t au_priority = std::numeric_limits< u32_t >::max )
	{
		DelegateType dlgt;
		dlgt.Set( ap_obj, ap_mem_fun );

		std::pair< CSet< DelegateType >::iterator, bool_t > inserted;
		inserted = m_s_unique_dlgt.insert( dlgt );
		if( !inserted.second )
			return false;

		DelegatePriority dp;
		dp.m_p_dlgt = dlgt;
		dp.m_u_priority = au_priority;
		m_ms_priority_dlgt.insert( dp );

		return true;
	}

	//! Register a delegate of the same type in the event
	bool_t Register( DelegateType& ar_dlgt, u32_t au_priority = std::numeric_limits< u32_t >::max( ) )
	{
		std::pair< CSet< DelegateType >::iterator, bool_t > inserted;
		inserted = m_s_unique_dlgt.insert( ar_dlgt );
		if( !inserted.second )
			return false;

		DelegatePriority dp;
		dp.m_dlgt = ar_dlgt;
		dp.m_u_priority = au_priority;
		m_ms_priority_dlgt.insert( dp );

		return true;
	}

	//! Unregister a delegate previously registered
	bool_t Unregister( const DelegateType& ar_dlgt )
	{
		size_type erase_count = m_s_unique_dlgt.erase( ar_dlgt );
		if( erase_count == 0 )
			return false;

		bool_t b_erased = false;
		CMultiSet::iterator it( m_ms_priority_dlgt.begin( ) );
		for( ; it != m_ms_priority_dlgt.end( ); ++it )
		{
			if( it->m_dlgt == ar_dlgt )
				b_erased = b_erased || ( m_ms_priority_dlgt.erase( it ) > 0 );
		}
		return b_erased;
	}

	//! Invoke all the delegates stored, return the last delegates's return value
	RETURN_TYPE Invoke( ARGUMENTS_TYPES_NAMES )
	{
		CMultiSet< DelegatePriority >::iterator it( m_ms_priority_dlgt.begin( ) );
		CMultiSet< DelegatePriority >::iterator end( m_ms_priority_dlgt.end( ) );
		--end; //Reserve the last delegate to return it
		DelegatePriority aux;
		for( ; it != end; ++it )
		{
			aux = *it;
			aux.m_dlgt.Invoke( ARGUMENTS_NAMES );
		}

		aux = *end;
		return aux.m_dlgt.Invoke( ARGUMENTS_NAMES );
	}

	//! Invoke all the delegates and store all the results in a vector
	CVector< RETURN_TYPE >& Invoke( CVector< RETURN_TYPE >& ar_v_results COMMA_SEPARATOR ARGUMENTS_TYPES_NAMES )
	{
		CMultiSet::iterator it( m_ms_priority_dlgt.begin( ) );
		CMultiSet::iterator end( m_ms_priority_dlgt.end( ) );
		for( ; it != end; ++it )
			ar_v_results.push_back( (*it).m_dlgt.Invoke( ARGUMENTS_NAMES ) );

		return ar_v_results;
	}

	//! Invoke all the delegates stored, returns the last delegate's return value
	RETURN_TYPE operator( ) ( ARGUMENTS_TYPES_NAMES )
	{
		return Invoke( ARGUMENTS_NAMES );
	}

	//! Register a delegate of the same type in the event
	bool_t operator += ( DelegateType& ar_dlgt )
	{
		return Register( ar_dlgt );
	}

	//! Unregister a delegate previously registered
	bool_t operator -= ( const DelegateType& ar_dlgt )
	{
		return Unregister( ar_dlgt );
	}
};